package edu.siu.math.egut.main;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Scanner;
import java.util.regex.Matcher;

import edu.siu.math.egut.io.Algorithms;
import edu.siu.math.egut.io.LiE;
import edu.siu.math.egut.io.OutputPref;
import edu.siu.math.egut.io.ParabolicException;
import edu.siu.math.egut.io.ParabolicPieceException;
import edu.siu.math.egut.io.SunipotentFormatException;
import edu.siu.math.egut.util.Group;
import edu.siu.math.egut.util.Matrix;
import edu.siu.math.egut.util.MatrixMultiplicationException;
import edu.siu.math.egut.util.NoSuchElementException;
import edu.siu.math.egut.util.NotARootException;
import edu.siu.math.egut.util.Polynomial;
import edu.siu.math.egut.util.PolynomialReadException;
import edu.siu.math.egut.util.Sunipotent;

/**
 * @author josephhundley
 *
 */
public class DualPieceRep_OLD {

    private static final String INSTRUCTIONS = "Syntax:  DualPieceRep Group Parabolic PieceSpecifier Elements";

    /**
     * Let P =MU be a parabolic subgroup of a reductive group G.  Let {i1 < i2 < ... < ik}.
     * Let r be the semisimple rank of G and identify roots of G with r-tuples of integers 
     * by writing them in terms of the basis of simple roots.  Define a mapping Z^r -> Z^k by 
     * (n[1], ... n[r]) ->( n[i1], ..., n[ik]).  
     * Each of the fibers of this map defines a M-module.  
     * A realization of G determines a basis for each of these M-modules.  The purpose of this 
     * program is to compute the matrices of elements of M with respect to these bases.
     * 
     * Syntax:  DualPieceRep Group Parabolic PieceSpecifier Elements 
     * 
     * @param args need to specify the group G, parabolic P, and which piece we consider, and finally an element of M.
     */
    public static void main(final String[] args) {
	/* TODO introduce tweaks for easier input-output.
	 * TODO check whether this is really a sound method for studying the action on characters.
	 * I suppose it's clear enough that the action on characters of U and the action on the
	 * Lie algebra piece this acts on are isomorphic representations, but what we want 
	 * is a good way to get a matrix which we can use to compute g.psi(u).  Not clear 
	 * that this is it.
	 * 
	 * TODO  something odd is causing this program and CharacterAction to produce 
	 * inconsistent results... 
	 * 
	 */
	
	// extract input from args
	String[] input;
	try {
	    input = Algorithms.getCommandsFromFile(args[0]);
	} catch (FileNotFoundException e1) {
	    System.out.println("File not found.  Please try again.");
	    try {
		input = Algorithms.getCommandsFromFile(Algorithms.getFileName(new Scanner(System.in)));
	    } catch (FileNotFoundException e) {
		
		input=new String[1];
		System.out.println("File not found.  Please try again.");
		e.printStackTrace();
		System.exit(1);
	    }
	} catch (ArrayIndexOutOfBoundsException e1) {
	    System.out.println("File not found.  Please try again.");
	    try {
		input = Algorithms.getCommandsFromFile(Algorithms.getFileName(new Scanner(System.in)));
	    } catch (FileNotFoundException e) {
		
		input=new String[1];
		System.out.println("File not found.  Please try again.");
		e.printStackTrace();
		System.exit(1);
	    }
	}
	
	
	// input[0] is supposed to be the name of a group.  Check that it is one. 
	if(input.length < 4 ){
	    System.out.println(INSTRUCTIONS);
	}
	final Group g = Group.getGroup(input[0]);
	if(g == null){
	    System.out.println("First argument --"+ input[0] + "-- is not the name of an exceptional group.");
	    System.exit(1);
	}
	try {
		/* input[1] should specify a parabolic subgroup of that exceptional group.  therefore, 
		 *   (1) every character in it should be a digit between 1 and the rank of g inclusive, and 
		 *   (2) they should all be different.
		 *   Those conditions being satisfied, the interpretation is that you are listing those simple roots
		 *   such that the corresponding root subgroup is in the unipotent radical.
		 */

	    
	    final String parabolicSpecifier = parseParabolic(input[1],g);
	    
	    /* input[2] should specify a piece.    Can do it in one of two ways 
	     * (a) an integer for each root listed in args[1]
	     * (b) a single integer.
	     * 
	     */
	    
	    final String pieceSpecifier = parsePiece(input[2], input[1].length());
	    
	    /*
	     * get list of roots in the specified piece from LiE
	     */
	    final LiE lie = new LiE(  );
	    final int[][] pieceRoots = Algorithms.readLiEMatrix(lie.exec("-piece("+pieceSpecifier+","+parabolicSpecifier+","+input[0]+")", OutputPref.HIDE));
	    /*Note the minus sign.  Because of this sign, LiE will return a list of negative roots.  This ensures that the subspace
	     * of the LiE algebra we act on is isomorphic to the DUAL of the piece specified, or, equivalently, to the space of 
	     * characters.  (See also 'character action.')
	     * 
	     * -piece (...) will return a list of roots such that b comes before a whenever b-a is a positive 
	     * root.  
	     * 
	     * This ensures that the matrix attached to x_c(r) is upper triangular
	     * when c is positive, in accordance with our intuition.
	     */
	    
	    /*
	     * TODO  current code crashes if an empty piece is specified because LiE matrix "null(0,8)"
	     * crashes readLiEMatrix.  This should probably be fixed.  First, because readLiEMatrix should
	     * be more robust in this regard, and because an empty piece most likely indicates a user 
	     * error where the parabolic specifier and the piece specifier have been reversed
	     * and this program should respond helpfully in that situation.
	     * 
	     * 
	     * 
	     */
	   
	    
	    /*Compute the matrix.  
	     * 
	     * It is convenient to think of the rows and columns of this matrix as being indexed the roots of T in our piece V. 
	     * 
	     * We describe how to compute the matrix for a toral element, for a simple unipotent, and for a weyl element.  
	     * This is, of course, plenty to compute it for an arbitrary element, by the Bruhat decomposition.  
	     * 
	     * For a toral it's easy.  Let t be the toral and b any root of T in V.  Write [g]_V for the matrix of g in V, 
	     * relative to the basis of V which is a subset of our fixed Chevalley basis.  Then [t]_V is diagonal 
	     * and for each root b the (b,b) entry is b(t).
	     * 
	     * Now take a root a of T in M and an element r of the additive group.  Take roots b and c of T in V.  Then the simplest
	     * thing to say is that X_a determines an endomorphism of V with matrix [X_a]_V such that the (b,c) entry is N(a,b) if 
	     * c=a+b and 0 otherwise.  The matrix of x_a(r) is then the matrix exponential of r X_a.
	     * 
	     * Finally, for a Weyl element the answer is easy to read from Weyl signs...
	     * 
	     * The matrix we seek will have entries which are Polynomials with integral coefficients except (obviously) in the 
	     * case of a toral element.  Therefore we implement only for the other two types for now, putting off possible implementation
	     * for torals or in general until such time as rational functions are fully implemented.  
	     * 
	     */
		Matrix answer = Matrix.id(pieceRoots.length);
		for( int i = 3; i < input.length; i++)
		    answer = answer.times(matrix(input[i], pieceRoots, g));
		
		if(args.length >1)
		System.out.println(answer.toString(args[1]));
		else
		    System.out.println(answer.toString());

	    
	    
	} catch (final ParabolicException e) {
	    // TODO Auto-generated catch block
	    System.out.println("Second argument -- "+input[1] +"-- could not be parsed as a parabolic in "+input[0]+".\n" +
	    		"required format is to list the simple roots that are in the unipotent radical,\n" +
	    		"all mashed together.  Example: 134");
	    e.printStackTrace();
	    
	} catch (final ParabolicPieceException e) {
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	} catch (final IOException e) {
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	} catch (final PolynomialReadException e) {
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	} catch (final NotARootException e) {
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	} catch (final NoSuchElementException e) {
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	} catch (final SunipotentFormatException e) {
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	} catch (final MatrixMultiplicationException e) {
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	}
	
	
	
    
    }
    
    private static Matrix matrix(final String string, final int[][] vRoots, final Group g) throws PolynomialReadException, NotARootException, NoSuchElementException, SunipotentFormatException, MatrixMultiplicationException {
	/* For now, this method should check whether the string specifies a Sunipotent, and if 
	 * not, check whether it specifies a Weyl element, 
	 * and if not, throw an exception.
	 */
	Matcher s = Algorithms.suniString(g).matcher(string);
	if(s.matches()){
	    final Sunipotent suni = Sunipotent.read(string,g);
	    return Matrix.matrixOf(suni, vRoots, g);
	    
	    
	}
	
	s=Algorithms.weylWord(g).matcher(string);
	
	if( s.matches()){
	    return Matrix.matrixOfWeylWord(string, vRoots, g);
	
	}
	throw new NoSuchElementException();
    }

    /*
     * Idea here is to check the format of a string which is supposed to specify a piece, 
     * and then parse it from a convenient-to-type format into a format for LiE.
     * 
     */
    private static String parsePiece(final String string, final int ParabolicLength) throws ParabolicPieceException {
	if((string.length() != 1)&&(string.length() != ParabolicLength) )
	    throw new ParabolicPieceException();
	final StringBuilder sb = new StringBuilder("[");
	for( int i =0; i < string.length(); i++)
	{
	    if(string.charAt(i)<48||string.charAt(i)>57){
		throw new ParabolicPieceException(  );
	    }
	    sb.append(string.charAt(i));
	    sb.append(i==string.length()-1?"]":",");
	}
	
	return sb.toString();

    
    }

    /*The purpose of this method is twofold:  (1) to validate user input, and (2) to allow the main program to 
     * accept input in the easiest-for-the-user format, yet have the string in the correct format for 
     * feeding to LiE.  
     * 
     */
    private static String parseParabolic(final String string, final Group g) throws ParabolicException {
	
	final StringBuilder sb = new StringBuilder("[");
	
	for( int i =0; i < string.length(); i++)
	{
	    if(string.charAt(i)<49||string.charAt(i)>48+g.getRank()){
		throw new ParabolicException(  );
	    }
	    sb.append(string.charAt(i));
	    sb.append(i==string.length()-1?"]":",");
	}
	
	return sb.toString();
    }

}
